鐵人賽開始也快過了三分之一了,回頭看了自己的文章,寫的還真是亂。這個星期六日有時間的話會把前面的文章再整理一下。
從這篇文章開始,會著重在解決前一篇文章所提到的各種web component的問題(以單一專案的小範圍開發為前提)。
一個完整的web componet(custom element + shadow DOM)最主要的目的就是要讓component的內外分離。所以外部無法直接存取或控制component內部的DOM(因為document.getElementById 之類的方法無法獲取web component內部的DOM)。
幸好,custom element是javascript和HTML element的結合,再加上shadow DOM的特性。可以使用以下的方法來改變內部的DOM。
在普通的HTML 元素中,
因為web component是使用custom element技術來建立,所以普通的HTML元素會有的屬性也可以在web component上使用。只要使用getAttribute這個方法就能獲得指定的屬性值
需要使用 custom element 生命周期的observedAttributes設定監看的屬性值,以及使用生命周期的attributeChangedCallback來改動Shadow DOM。
因為 web component 有使用到javascript,所以也可以使用javascript的方法來改動Shadow DOM。
html
<-- 只有設定'card-content'這個屬性 -->
<my-com id="my-com" card-content="ha! you~"></my-com>
Javascript
class MyComponent extends HTMLElement {
constructor() {
super();
this.render = this.render.bind(this);
this.attachShadow({mode: 'open'});
this.shadowRoot.appendChild(this.render())
}
connectedCallback() {
}
static get observedAttributes() {
// 只有'card-content'這個屬性要監看變化
return ['card-content'];
}
attributeChangedCallback(name, oldValue, newValue) {
// 當'card-content'這個屬性變化時使用
if (name === 'card-content') {
this.shadowRoot.getElementById('card-content').innerHTML = newValue;
}
}
set cardContent(value) {
// 如果有和屬性連動的話,建議只改屬性值,實際的變化留到attributeChangedCallback中再修改
this.setAttribute('card-content', value);
}
get cardContent() {
return this.getAttribute('card-content');
}
set cardTitle(value) {
// 因為沒有和屬性連動的話,可以直接變動shadow DOM
this.shadowRoot.getElementById('card-title').innerHTML = value;
}
get cardTitle() {
return this.shadowRoot.getElementById('card-title').innerHTML;
}
render() {
const template = document.createElement('template');
template.innerHTML = `
<div>
<h2 id='card-title'>title</h2>
<div id='card-content'></div>
</div>
`;
return template.content;
}
}
customElements.define('my-com', MyComponent);
const myComNode = document.getElementById('my-com');
console.log(myComNode.cardContent) //ha! you~
console.log(myComNode.cardTitle) //title
myComNode.cardContent = 'hallo world'
myComNode.cardTitle = 'Title2'
console.log(myComNode.cardContent) //hallo world
console.log(myComNode.cardTitle) //Title2